#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;
//using System.Windows;
//using AdRotatorXNA;
#endregion

namespace Starter3DGame
{
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class Starter3DGameplayScreen : GameScreen
    {
        #region Fields
        ContentManager content;

		// The aspect ratio determines how to scale 3d to 2d projection.
		float aspectRatio;

		//Camera_View information
		Vector3 cameraPosition = new Vector3(0.0f, 0.0f, GameConstants.CameraHeight);
		static Matrix projectionMatrix;
		static Matrix viewMatrix;

		SpriteFont Font;
		Random random = new Random();

		//Player Ship
		Ship ship = new Ship();

		//Asteroid Template
		Asteroid[] asteroidList;
		Matrix[] AsteroidsTransforms;
		Model AsteroidModel;

		//Bullet Template
		Model BulletModel;
		Texture2D BulletTexture;
		Matrix[] bulletTransforms;
		static Bullet[] bulletList = new Bullet[GameConstants.NumBullets];
		//Background
		Texture2D stars;

		public static int PreferredBackBufferWidth;
		public static int PreferredBackBufferHeight;

		public static Viewport viewport;

		public static SoundEffect Engine;
		public static SoundEffectInstance EngineInstance;

		public static SoundEffect Shot;

		public SoundEffect ExplosionShip;
		public SoundEffect ExplosionAsteroid;
		public SoundEffect HyperSpace;


		public SoundEffect Music;
		public SoundEffectInstance MusicInstance;

		public int Score = 0;
		public float TimetoLive = 0;
		public int PlayerLives = 0;
		public int Level = 1;



        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public Starter3DGameplayScreen()
        {
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);

            //Dont forget to specify the gestures you want to use.
            //EnabledGestures = GestureType.Hold;

			//Added
            if (MediaPlayer.GameHasControl)
            {
				GameConstants.canPlay = true;
            }

            //Specify where you want the Ad to display (based on the top left corner of the Ad)
            AdRotatorXNAComponent.Current.AdPosition = Vector2.Zero;
        }

		/// <summary>
		/// Helper method to the initialize the game to be a portrait game.
		/// </summary>
		private void InitializeDownscaleGraphics()
		{
			ScreenManager.game.Graphics.SupportedOrientations = DisplayOrientation.Portrait | DisplayOrientation.LandscapeLeft;
			PreferredBackBufferWidth = 240;
			PreferredBackBufferHeight = 400;

		}


		private void ResetAsteroids()
		{
			float xStart;
			float yStart;
			asteroidList = new Asteroid[GameConstants.NumAsteroids * Level];
			for (int i = 0; i < GameConstants.NumAsteroids * Level; i++)
			{
				asteroidList[i] = new Asteroid();
				if (random.Next(2) == 0)
				{
					xStart = (float)-GameConstants.PlayfieldSizeX;
				}
				else
				{
					xStart = (float)GameConstants.PlayfieldSizeX;
				}
				yStart = (float)random.NextDouble() * GameConstants.PlayfieldSizeY;
				asteroidList[i].Position = new Vector3(xStart, yStart, 0.0f);
				double angle = random.NextDouble() * 2 * Math.PI;
				asteroidList[i].Direction.X = -(float)Math.Sin(angle);
				asteroidList[i].Direction.Y = (float)Math.Cos(angle);
				asteroidList[i].Speed = GameConstants.AsteroidMinSpeed +
				   (float)random.NextDouble() * GameConstants.AsteroidMaxSpeed;
				asteroidList[i].Rotation.X = (float)random.NextDouble();
				asteroidList[i].Rotation.Y = (float)random.NextDouble();
				asteroidList[i].Rotation.Z = (float)random.NextDouble();
				asteroidList[i].isActive = true;
			}
		}

		private Matrix[] SetupEffectDefaults(Model myModel)
		{
			Matrix[] absoluteTransforms = new Matrix[myModel.Bones.Count];
			myModel.CopyAbsoluteBoneTransformsTo(absoluteTransforms);

			foreach (ModelMesh mesh in myModel.Meshes)
			{
				foreach (BasicEffect effect in mesh.Effects)
				{
					effect.EnableDefaultLighting();
					effect.Projection = projectionMatrix;
					effect.View = viewMatrix;
					effect.PreferPerPixelLighting = false;
				}
			}
			return absoluteTransforms;
		}

		private Model SetupModelTexture(Model myModel, Texture2D myTexture)
		{

			foreach (ModelMesh mesh in myModel.Meshes)
			{
				foreach (BasicEffect effect in mesh.Effects)
				{
					effect.TextureEnabled = true;
					effect.Texture = myTexture;
				}
			}
			return myModel;

		}

        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

			InitializeDownscaleGraphics();
			ScreenManager.game.Graphics.PreferredBackBufferWidth = PreferredBackBufferWidth;
			ScreenManager.game.Graphics.PreferredBackBufferHeight = PreferredBackBufferHeight;
			viewport = ScreenManager.game.Graphics.GraphicsDevice.Viewport;
			ResetAsteroids();

			PlayerLives = GameConstants.PlayerLives;

			// Load Font
			Font = content.Load<SpriteFont>(@"Fonts\SpriteFont1");

			//Setup Camera
			aspectRatio = ScreenManager.Game.GraphicsDevice.Viewport.AspectRatio;

			projectionMatrix = Matrix.CreatePerspectiveFieldOfView(
			  MathHelper.ToRadians(45.0f), aspectRatio,
			  GameConstants.CameraHeight - 100.0f,
			  GameConstants.CameraHeight + 100.0f);

			viewMatrix = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, Vector3.Up);

			//Load Models
			ship.Model = content.Load<Model>(@"Models\p1_wedge");
			ship.Transforms = SetupEffectDefaults(ship.Model);

			AsteroidModel = content.Load<Model>(@"Models\asteroid1");
			AsteroidsTransforms = SetupEffectDefaults(AsteroidModel);

			BulletModel = content.Load<Model>(@"Models\pea_proj");
			BulletTexture = content.Load<Texture2D>(@"Textures\pea_proj");
			bulletTransforms = SetupEffectDefaults(BulletModel);
			BulletModel = SetupModelTexture(BulletModel, BulletTexture);

			//Load Background
			stars = content.Load<Texture2D>(@"Textures\B1_stars");

			//Load Audio
			Engine = content.Load<SoundEffect>(@"Audio\EngineSound");
			EngineInstance = Engine.CreateInstance();
			//EngineInstance.IsLooped = true;

			Shot = content.Load<SoundEffect>(@"Audio\Fire");
			ExplosionShip = content.Load<SoundEffect>(@"Audio\ExplosionShip");
			ExplosionAsteroid = content.Load<SoundEffect>(@"Audio\ExplosionAsteroid");
			HyperSpace = content.Load<SoundEffect>(@"Audio\hyperspace_activate");

			Music = content.Load<SoundEffect>(@"Audio\Music");
			MusicInstance = Music.CreateInstance();
			MusicInstance.Volume = 0.9f;
			MusicInstance.IsLooped = true;
            if (GameConstants.canPlay) MusicInstance.Play();



            // once the load has finished, we use ResetElapsedTime to tell the game's
            // timing mechanism that we have just finished a very long frame, and that
            // it should not try to catch up.
            ScreenManager.Game.ResetElapsedTime();
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            content.Unload();
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Updates the state of the game. This method checks the GameScreen.IsActive
        /// property, so the game will stop updating when the pause menu is active,
        /// or if you tab away to a different application.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

            if (IsActive)
            {
				float time = (float)gameTime.ElapsedGameTime.TotalMilliseconds;


				//Is ship still alive, if so update.
				if (ship.isActive)
					ship.Update(gameTime);
				else
				{
					if (PlayerLives < 0)
					{
						
						string messageText = "\n\n Game Over " +
									 "\n \n Final Score:  " + Score + 
									 "\n \n Level:  " + Level;
						
						ExitScreen();
						ScreenManager.AddScreen(new BackgroundScreen(), PlayerIndex.One);
						ScreenManager.AddScreen(new MainMenuScreen(), PlayerIndex.One);
						ScreenManager.AddScreen(new MessageBoxScreen(messageText, false), PlayerIndex.One);
					}
					time = time / 100;
					if (time >= TimetoLive)
					{
						ship.isActive = true;
						HyperSpace.Play();
					}
					else
					{
						TimetoLive -= time;
					}
				}

				// Are there any asteroids left, if so update
				bool Asteroidsleft = false;
				for (int i = 0; i < asteroidList.Length; i++)
				{
					if (asteroidList[i].isActive)
					{
						asteroidList[i].Update(gameTime);
						Asteroidsleft = true;
					}
				}
				if (!Asteroidsleft)
				{
					Level++;
					Level = (int)MathHelper.Clamp(Level, 1, GameConstants.MaxLevel);
					ResetAsteroids();

				}


				for (int i = 0; i < GameConstants.NumBullets; i++)
				{
					if (bulletList[i].isActive)
					{
						bulletList[i].Update(gameTime);
					}
				}

				CheckCollisions();
            }
        }


        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // Look up inputs for the active player profile.
            int playerIndex = (int)ControllingPlayer.Value;

             // if the user pressed the back button, we return to the main menu
            PlayerIndex player;
            if (input.IsNewButtonPress(Buttons.Back, ControllingPlayer, out player))
            {
                LoadingScreen.Load(ScreenManager, false, ControllingPlayer, new BackgroundScreen(), new MainMenuScreen());
            }


        }


        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            // This game has a blue background. Why? Because!
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
                                               Color.CornflowerBlue, 0, 0);

            // Our player and enemy are both actually just text strings.
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
			GraphicsDevice graphicsDevice = ScreenManager.Game.GraphicsDevice;

			// TODO: Add your drawing code here
			spriteBatch.Begin();
			spriteBatch.Draw(stars, graphicsDevice.Viewport.TitleSafeArea, Color.White);
			spriteBatch.DrawString(Font, "Lives: " + PlayerLives, new Vector2(10, 10), Color.Red);
			spriteBatch.DrawString(Font, "Score: " + Score, new Vector2(10, 40), Color.Red);
			spriteBatch.DrawString(Font, "Level: " + Level, new Vector2(10, 70), Color.Green);
			if (!ship.isActive) spriteBatch.DrawString(Font, "Time to Live: " + (int)(TimetoLive + 10) / 10, new Vector2(10, 100), Color.Red);
			spriteBatch.End();

			graphicsDevice.BlendState = BlendState.Opaque;
			graphicsDevice.DepthStencilState = DepthStencilState.Default;
			graphicsDevice.SamplerStates[0] = SamplerState.LinearWrap;

			if (ship.isActive)
			{
				DrawModel(ship.Model, ship.TransformMatrix, ship.Transforms, GameConstants.ShipScale);
			}

			for (int i = 0; i < asteroidList.Length; i++)
			{
				if (asteroidList[i].isActive)
				{
					DrawModel(AsteroidModel, asteroidList[i].TransformMatrix, AsteroidsTransforms, GameConstants.AsteroidScale);
				}
			}

			for (int i = 0; i < GameConstants.NumBullets; i++)
			{
				if (bulletList[i].isActive)
				{
					Matrix bulletTransform =
					  Matrix.CreateTranslation(bulletList[i].Position);
					DrawModel(BulletModel, bulletTransform, bulletTransforms, GameConstants.BulletScale);
				}
			}


            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0)
                ScreenManager.FadeBackBufferToBlack(1f - TransitionAlpha);
        }

		public static void DrawModel(Model model, Matrix modelTransform, Matrix[] absoluteBoneTransforms)
		{
			DrawModel(model, modelTransform, absoluteBoneTransforms);
		}

		public static void DrawModel(Model model, Matrix modelTransform, Matrix[] absoluteBoneTransforms, float scale)
		{
			//Draw the model, a model can have multiple meshes, so loop
			foreach (ModelMesh mesh in model.Meshes)
			{
				//This is where the mesh orientation is set
				foreach (BasicEffect effect in mesh.Effects)
				{
					effect.World = Matrix.CreateScale(scale) * absoluteBoneTransforms[mesh.ParentBone.Index] * modelTransform;
				}
				//Draw the mesh, will use the effects set above.
				mesh.Draw();
			}
		}

		public static Vector2 TransformtoScreenSpace(Vector2 position)
		{
			//Create a 3D vector from our 2D touch
			Vector3 point = new Vector3(position, 0f);

			// use Viewport.Unproject to tell what those two screen space positions
			// would be in world space. we'll need the projection matrix and view
			// matrix, which we have saved as member variables. We also need a world
			// matrix, which can just be identity.
			Vector3 target = viewport.Unproject(point,
				projectionMatrix, viewMatrix, Matrix.Identity);

			position.X = target.X;
			position.Y = target.Y;

			return position;
		}

		public static void Add(Bullet bullet)
		{
			bullet.isActive = true;
			//add another bullet.  Find an inactive bullet slot and use it
			//if all bullets slots are used, ignore the user input
			for (int i = 0; i < GameConstants.NumBullets; i++)
			{
				if (!bulletList[i].isActive)
				{
					bulletList[i] = bullet;
					Shot.Play();
					break; //exit the loop     
				}
			}
		}

		public void CheckCollisions()
		{
			//Check Ship against Asteroids
			if (ship.isActive)
			{
				for (int i = 0; i < asteroidList.Length; i++)
				{
					if (asteroidList[i].isActive)
					{
						//Test Distance
						float distance = Vector3.Distance(ship.Position, asteroidList[i].Position);
						if (distance <= GameConstants.ShipCollisionSize || distance <= GameConstants.AsteroidCollisionSize)
						{
							//Collided
							DestroyShip();
							asteroidList[i].isActive = false;
							break;
						}
					}
				}
			}

			//Check Ship against Bullets
			if (ship.isActive)
			{
				for (int i = 0; i < GameConstants.NumBullets; i++)
				{
					if (bulletList[i].isActive)
					{
						//Test Distance
						float distance = Vector3.Distance(ship.Position, bulletList[i].Position);
						if (distance <= GameConstants.ShipCollisionSize || distance <= GameConstants.BulletCollisionSize)
						{
							//Collided
							DestroyShip();
							bulletList[i].isActive = false;
							break;
						}
					}
				}
			}

			//Check Asteroids against Asteroids
			for (int i = 0; i < asteroidList.Length; i++)
			{
				if (asteroidList[i].isActive)
				{
					for (int j = 0; j < asteroidList.Length; j++)
					{
						if (asteroidList[j].isActive && j > i)
						{
							//Test Distance
							float distance = Vector3.Distance(asteroidList[j].Position, asteroidList[i].Position);
							if (distance <= GameConstants.AsteroidCollisionSize)
							{
								//Collided
								BounceAsteroid();
							}

						}
					}

				}
			}

			//Check Asteroids agsinst Bullets
			for (int i = 0; i < asteroidList.Length; i++)
			{
				if (asteroidList[i].isActive)
				{
					for (int j = 0; j < GameConstants.NumBullets; j++)
					{
						if (bulletList[j].isActive)
						{
							//Test Distance
							float distance = Vector3.Distance(asteroidList[i].Position, bulletList[j].Position);
							if (distance <= GameConstants.AsteroidCollisionSize || distance <= GameConstants.BulletCollisionSize)
							{
								//Collided
								asteroidList[i].isActive = false;
								bulletList[j].isActive = false;
								ExplosionAsteroid.Play();
								Score++;
							}

						}
					}
				}
			}
		}

		public void DestroyShip()
		{
			ship.isActive = false;
			ship.Position = Vector3.Zero;
			ship.Velocity = Vector3.Zero;
			ExplosionShip.Play();
			PlayerLives--;
			TimetoLive = GameConstants.ShipSpawnTime;
		}

		public void BounceAsteroid()
		{
			//Work out new direction for each asteroid and send them on their way
		}


        #endregion
    }
}
